package com.huterox.spring.framework.context;

import com.huterox.spring.framework.annotation.HUAutowired;
import com.huterox.spring.framework.annotation.HUController;
import com.huterox.spring.framework.annotation.HUService;
import com.huterox.spring.framework.beans.HUBeanWrapper;
import com.huterox.spring.framework.beans.suports.HUBeanDefinitionReader;
import com.huterox.spring.framework.beans.config.HUBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class HUApplicationContext {

    private String[] configLocations;
    private HUBeanDefinitionReader reader;

    private Map<String, HUBeanDefinition> beanDefinitionMap = new HashMap<>();
    private Map<String, HUBeanWrapper> factoryBeanInstanceCache = new HashMap<>();
    private Map<String, Object> factoryBeanObjectCache = new HashMap<>();

    public HUApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
        //加载配置文件读取器
        this.reader = new HUBeanDefinitionReader(this.configLocations);
        List<HUBeanDefinition> beanDefinitions = this.reader.doLoadBeanDefinitions();
        //2.缓存beanDefinitions对象
        try {
            doRegistryBeanDefintition(beanDefinitions);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //3.创建IOC容器,把类都放在IOC容器里面
        doCreateBean();
    }

    private void doCreateBean() {
        for (Map.Entry<String, HUBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            //非延时加载
            getBean(beanName);
        }
    }

    private void doRegistryBeanDefintition(List<HUBeanDefinition> beanDefinitions) throws Exception {
        //把Definition对象放在咱们的map里面,beanDefinition是保存的类名，全包名
        for (HUBeanDefinition beanDefinition : beanDefinitions) {
            //双键存储便于双向查找
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is exists!");
            }
            this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            this.beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    //创建Bean的实例，完成依赖注入
    public Object getBean(String beanName) {
        //得到对象的全包名,当然这个信息封装在beanDefinition里面
        HUBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //实例化
        Object instance = instanceBean(beanName, beanDefinition);
        if (instance == null) return null;
        //实例封装为beanWrapper当中
        HUBeanWrapper beanWrapper = new HUBeanWrapper(instance);
        //将这个wrapper对象放在容器里面IOC里面
        this.factoryBeanInstanceCache.put(beanName, beanWrapper);
        //完成依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);
        return this.factoryBeanInstanceCache.get(beanName).getWrapperInstance();
    }

    private void populateBean(String beanName, HUBeanDefinition beanDefinition, HUBeanWrapper beanWrapper) {
        //开始做依赖注入给值
        Object instance = beanWrapper.getWrapperInstance();
        Class<?> clazz = beanWrapper.getWrapperClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!(field.isAnnotationPresent(HUAutowired.class))) continue;
            HUAutowired autowired = field.getAnnotation(HUAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }
            field.setAccessible(true);

            try {
                if (!this.factoryBeanInstanceCache.containsKey(autowiredBeanName)) continue;
                //这个就是为什么要按照标准来写首字母要小写
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    private Object instanceBean(String beanName, HUBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            Class<?> clazz = Class.forName(className);
            if (!(clazz.isAnnotationPresent(HUService.class) || clazz.isAnnotationPresent(HUController.class))) {
                return null;
            }
            instance = clazz.newInstance();
            //接下来的这部分是AOP的入口，先忽略


            //三级缓存，为了解决这种循环依赖的问题，所以在后面进行DI的时候，这里
            //先进行一个缓存。因为完整的生命周期是对象要完成DI注入后，如果没有先进行缓存
            //那么在同一时刻进行DI注入的时候，出现A依赖B，B依赖A，并且缓存池里面没有B，要创建B
            //然后B又要A，A因为没有B又创建不了的情况，先初步缓存这样A要B的时候，创建B然后要依赖A
            //此时A在创建的时候有个缓存,这样B可以被创建，然后A最后完成注入，同样B也会完成。这部分逻辑后面再说
            this.factoryBeanObjectCache.put(beanName, instance);


        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return instance;
    }

    public Object getBean(Class className) {
        return getBean(className.getName());
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}
